extends Node

# WebSocket - клиент
class_name GraphQLWSConnector


# Signal to let GUI know whats up
signal connection_failed()
signal connection_succeeded()
signal server_disconnected()
signal input_data()


signal tempo_informpetoj() # изменилось среднее время вызова


# настройки по справочнику
# константы соответствия категорий, статусов и т.п.
# категории задач:
const kategorio_movado = 3 # категория движения объекта
const kategorio_eniri_kosmostacio = 8 # категория входа в станцию
const kategorio_eliro_kosmostacio = 9 # категория выхода из станции
const tasko_kategorio_celilo = 6 # категория задачи прицеливания
const tasko_kategorio_pafo = 7 # категория задачи выстрела
const projekto_kategorio_pafado = 6 # категория проекта стрельбы (включает задачи прицеливания и выстрела)
const projekto_tipo_objekto = 2 # тип проектов "Для объектов"
const tasko_tipo_objekto = 2 # тип задач "Для объектов"
const statuso_nova = 1 # статус "Новый"
const statuso_laboranta = 2 # статус "В работе"
const statuso_fermo = 4 # статус "Закрыт"
const status_pauzo = 6 # статус "Приостановлен"
const statuso_posedanto = 1 # статус владельца "Активный"
const tipo_posedanto = 1 # тип владения "Полное владение"


# пришедшие данные с сервера
var data_server = []


# запросы отправляются с очередным id 
var current_query_id = 1

var net_id_clear = [] # список пустых id запросов (ответы не анализируются, а удаляются)
var net_sendij = [] # список отправленных запросов с их id, именем запроса, временем отправки
# id, nomo_informpeto, tempo_sendij

# к какому серверу подключаться
var url_server = null
var http = "https"
var ws = "wss"
var websocket_url = "" 
#var _client = WebSocketClient.new()
var connected = false
var socket = WebSocketPeer.new()


# Хэш со всеми игроками зарегистрированными на сервере {id: name}
# заполняется функцией register_player()
var players = {}


# словарь среднего времени запросов к серверу
var tempo_informpeto = {} #время_запрос информации
var flago_tempo_informpeto = false # флаг, что производилась запись. Для регулярной записи в файл. Запись в файл сбрасывает данный флаг


func _ready():
	if not url_server:
		url_server = "t34.universo.pro"
		## url_server = "t54.tehnokom.su"
		## url_server = "t34.tehnokom.su"
		## url_server = "t34-85.tehnokom.su"
	websocket_url = ws+"://"+url_server+"/api/v1.1/ws/"


func _closed(was_clean = false):
	# для разрыва со стороны сервера - переподключаемся
	print("Closed, clean: ", was_clean)
	connected = false
	set_process(false) # Stop processing.
	emit_signal("server_disconnected")
	connect_to_server()


func _error(was_clean = false):
	# разрыв из-за ошибки - переподключаемся
	print("Closed, error: ", was_clean)
	connected = false
	emit_signal("connection_failed")
	connect_to_server()


func connect_to_server():
	# print('===input WS')
	socket.supported_protocols = PackedStringArray(["graphql-ws"])
	socket.handshake_headers = PackedStringArray(["Content-Type: application/json", "Referer: " + websocket_url])
	var err = socket.connect_to_url(websocket_url)
	if err != OK:
		print("Ошибка соединения = ", err)
	else:
		print('==WS установлен== ')
		connected = true
		set_process(connected)
		emit_signal("connection_succeeded")
	return err


# обработка полученных данных с сервера
func _on_data(data):
	#print('= on_data = ', data)
	if data.has("type") && data['type'] == 'data':
		# проверяем, нужно ли обрабатывать данный id
		var index_net = net_id_clear.find(int(data['id']))
		var sendi = sercxo_net_sendij(int(data['id']))
		if sendi > -1:
			# если есть в списке отправленных запросов для статистики
			add_tempo_informpeto(net_sendij[sendi]['nomo_informpeto'], 
				Time.get_ticks_msec() - net_sendij[sendi]['tempo'])
			net_sendij.remove_at(sendi)
		if index_net > -1: # находится в списке очищаемых запросов
			net_id_clear.remove_at(index_net)
		else:
			data_server.append(data)
			emit_signal("input_data")


func _process(_delta):
	# Что бы получать данные с сервера
	if connected:
		socket.poll()
		var state = socket.get_ready_state()
		if state == WebSocketPeer.STATE_OPEN:
			while socket.get_available_packet_count():
				var str = socket.get_packet().get_string_from_utf8()
				#print("Packet: ", str, " == ", typeof(str))
				var dd = JSON.parse_string(str)
				_on_data(dd)
		elif state == WebSocketPeer.STATE_CLOSING:
			# Продолжайте опрос, чтобы добиться надлежащего закрытия.
			pass
		elif state == WebSocketPeer.STATE_CLOSED:
			var code = socket.get_close_code()
			var reason = socket.get_close_reason()
			print("WebSocket closed with code: %d, reason %s. Clean: %s" % [code, reason, code != -1])
			#set_process(false) # Stop processing.
			_closed()


func _exit_tree():
	if connected:
		socket.close(3)
		connected = false


# отправка данных на сервер
func send_data(data, id=0): # если id==0 то используем current_query_id
	if !id:
		id = current_query_id
		current_query_id += 1
		add_net_sendij(id,'send_data')
	var query = JSON.stringify({
		'type': 'start',
		'id': '%s' % id,
		'payload': {'query':data}
	})
	socket.send_text(query)
	#_client.get_peer(1).put_packet(query.to_utf8())
	return id


# отправка данных на сервер
func send_json(data):
	var err = socket.send_text(data)
	if err!=OK:
		print('==== ОШИБКА ОТПРАВКИ = ', data)
	#_client.get_peer(1).put_packet(data.to_utf8())


# передаёт корректный очередной номер и увеличивает его для следующей передачи
func get_current_query_id():
	var id = current_query_id
	current_query_id += 1
	return id


func get_current_query_id_and_liberigi():
	"""
	передаёт корректный очередной номер, увеличивает его для следующей передачи и ответ ставит в очередь на удаление из списка полученных ответов
	liberigi - очистить  (от чего-л. занимающего место; от чего-л. мешающего) 
	"""
	var id = get_current_query_id()
	net_id_clear.append(id)
	return id 


func add_net_sendij(id, nomo_informpeto):
	net_sendij.append({'id': id,
		'nomo_informpeto': nomo_informpeto,
		'tempo': Time.get_ticks_msec()})


# поиск по id в списке отправленных запросов
# возвращает порядковый номер в массиве, если нет, то возвращает -1
func sercxo_net_sendij(id):
	var index = -1
	var pasxo = 0 # шаг
	for sendi in net_sendij:
		if id == sendi['id']:
			index = pasxo
			return index
		pasxo += 1
	return -1

# добавляем в словарь отправленное время запроса
# nomo_informpeto - имя запроса
# tempo - прошедшее время запроса
func add_tempo_informpeto(nomo_informpeto, tempo):
	var nomo = tempo_informpeto.get(nomo_informpeto)
	if nomo:
		var kvanto = nomo['kvanto'] + 1
		var tempoj = nomo['tempo'] + tempo
		tempo_informpeto[nomo_informpeto] = {
			'kvanto': kvanto,
			'tempo': tempoj,
			'averagxo': tempoj / kvanto #averaĝo - среднее
		}
	else:
		tempo_informpeto[nomo_informpeto] = {
			'kvanto': 1,
			'tempo': tempo,
			'averagxo': tempo
		}
	flago_tempo_informpeto = true
	emit_signal("tempo_informpetoj")
